{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#!/usr/bin/env python\n",
    "# -*- coding: utf-8 -*-\n",
    "\n",
    "\n",
    "\"\"\"\n",
    "实现以Json数据格式进行交换的RESTful API\n",
    "设计原因：\n",
    "    由于API就是把Web App的功能全部封装了，所以，通过API操作数据，\n",
    "    可以极大地把前端和后端的代码隔离，使得后端代码易于测试，\n",
    "    前端代码编写更简单\n",
    "实现方式：\n",
    "    一个API也是一个URL的处理函数，我们希望能直接通过一个@api来\n",
    "    把函数变成JSON格式的REST API， 因此我们需要实现一个装饰器，\n",
    "    由该装饰器将 函数返回的数据 处理成 json 格式\n",
    "\"\"\"\n",
    "\n",
    "import json\n",
    "import logging\n",
    "import functools\n",
    "\n",
    "from transwarp.web import ctx\n",
    "\n",
    "\n",
    "def dumps(obj):\n",
    "    \"\"\"\n",
    "    Serialize ``obj`` to a JSON formatted ``str``.\n",
    "    序列化对象\n",
    "    \"\"\"\n",
    "    return json.dumps(obj)\n",
    "\n",
    "\n",
    "class APIError(StandardError):\n",
    "    \"\"\"\n",
    "    the base APIError which contains error(required), data(optional) and message(optional).\n",
    "    存储所有API 异常对象的数据\n",
    "    \"\"\"\n",
    "    def __init__(self, error, data='', message=''):\n",
    "        super(APIError, self).__init__(message)\n",
    "        self.error = error\n",
    "        self.data = data\n",
    "        self.message = message\n",
    "\n",
    "\n",
    "class APIValueError(APIError):\n",
    "    \"\"\"\n",
    "    Indicate the input value has error or invalid. The data specifies the error field of input form.\n",
    "    输入不合法 异常对象\n",
    "    \"\"\"\n",
    "    def __init__(self, field, message=''):\n",
    "        super(APIValueError, self).__init__('value:invalid', field, message)\n",
    "\n",
    "\n",
    "class APIResourceNotFoundError(APIError):\n",
    "    \"\"\"\n",
    "    Indicate the resource was not found. The data specifies the resource name.\n",
    "    资源未找到 异常对象\n",
    "    \"\"\"\n",
    "    def __init__(self, field, message=''):\n",
    "        super(APIResourceNotFoundError, self).__init__('value:notfound', field, message)\n",
    "\n",
    "\n",
    "class APIPermissionError(APIError):\n",
    "    \"\"\"\n",
    "    Indicate the api has no permission.\n",
    "    权限 异常对象\n",
    "    \"\"\"\n",
    "    def __init__(self, message=''):\n",
    "        super(APIPermissionError, self).__init__('permission:forbidden', 'permission', message)\n",
    "\n",
    "\n",
    "def api(func):\n",
    "    \"\"\"\n",
    "    A decorator that makes a function to json api, makes the return value as json.\n",
    "    将函数返回结果 转换成json 的装饰器\n",
    "    @api需要对Error进行处理。我们定义一个APIError，\n",
    "    这种Error是指API调用时发生了逻辑错误（比如用户不存在）\n",
    "    其他的Error视为Bug，返回的错误代码为internalerror\n",
    "    @app.route('/api/test')\n",
    "    @api\n",
    "    def api_test():\n",
    "        return dict(result='123', items=[])\n",
    "    \"\"\"\n",
    "    @functools.wraps(func)\n",
    "    def _wrapper(*args, **kw):\n",
    "        try:\n",
    "            r = dumps(func(*args, **kw))\n",
    "        except APIError, e:\n",
    "            r = json.dumps(dict(error=e.error, data=e.data, message=e.message))\n",
    "        except Exception, e:\n",
    "            logging.exception(e)\n",
    "            r = json.dumps(dict(error='internalerror', data=e.__class__.__name__, message=e.message))\n",
    "        ctx.response.content_type = 'application/json'\n",
    "        return r\n",
    "    return _wrapper\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    import doctest\n",
    "    doctest.testmod()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
